/*
 * Copyright (C) <2009> <fscops> <Rishi B Agrawal> (postrishi@gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version->
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE->  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/highmem.h>
#include <linux/sched.h>

#define MAX_EXTENTION_LENGTH 5
#define OHSM_MAX_TIERS 100

/*
 * Data structure used for allocation policy.
 */

struct ohsm_uid_tier {
	uid_t uid;					/* user id */
	unsigned char tier;				/* respective tier of the user */
};

struct ohsm_gid_tier {
	gid_t gid;		 			/* group id*/
	unsigned char tier;				/* respective tier of the group */
};

struct ohsm_type_tier {
	char type[MAX_EXTENTION_LENGTH];		/* file extention */
	unsigned  char tier;				/* respective tier for the file type */
};

struct ohsm_allocation_policy {
	struct ohsm_uid_tier uid_tier[OHSM_MAX_TIERS];		/* address of dyamically created ohsm_uid_tier */
	struct ohsm_gid_tier gid_tier[OHSM_MAX_TIERS];		/* address of dynamically created ohsm_gid_tier */
	struct ohsm_type_tier type_tier[OHSM_MAX_TIERS];	/* address of dynamically created ohsm_type_tier*/
	int uidx;
	int gidx;
	int tidx;
	int cnt_users;					/* count of users for which tier has been specified*/
	int cnt_groups;					/* count of groups for which tier has been specified*/
	int cnt_types;					/* count of file types for which tier has been specified*/
};

extern struct ohsm_allocation_policy *ohsm_allocpol;

/* OHSM allocation test */

/************************************************************
 *
 * get_tier_name2 () compares the type of created file
 * with the given allocation policy file
 *
 * returns the tier-id specified in the allocation policy
 * file else zero for default allocation
 *
 ***********************************************************/
unsigned char get_tier_name2(char *namex)
{
	int i;
	BUG_ON(!namex);
	for (i = 0; i < ohsm_allocpol->cnt_types; i++)
		if (!strcmp(namex, ohsm_allocpol->type_tier[i].type))
			return ohsm_allocpol->type_tier[i].tier;
	return 0;
}

/***********************************************************
 *
 * get_tier_name () extracts the extension out of the
 * name and call get_tier_name2
 *
 * returns the tier-id specified in the allocation policy
 * file else zero for default allocation
 *
 ***********************************************************/
unsigned char get_tier_name(char *nam, unsigned int len)
{
	unsigned int i;
	unsigned char temp;

	BUG_ON(!nam);

	if (ohsm_allocpol->cnt_types == 0)
		return 0;

	/* If it is not null terminated we will crash */
	//BUG_ON(nam[len] != '\0');
	for (i = len - 1; i >= 0; i--) {
		if (*(nam + i) == '.') {
			temp = get_tier_name2((char *) (nam + i + 1));
			return temp;
		}
	}
	return 0;
}

/************************************************************
 *
 * get_tier_uid () compares the current uid with the
 * given uid in allocation policy file
 *
 * returns the tier-id specified in the allocation policy
 * file else zero for default allocation
 *
 ***********************************************************/

unsigned char get_tier_uid(uid_t uidx)
{
	int i;
	if (ohsm_allocpol->cnt_users == 0)
		return 0;

	for (i = 0; i < ohsm_allocpol->cnt_users; i++)
		if (uidx == ohsm_allocpol->uid_tier[i].uid)
			return ohsm_allocpol->uid_tier[i].tier;
	return 0;
}

/***********************************************************
 *
 * get_tier_gid () compares the current gid with the
 * given gid in allocation policy file
 *
 * returns the tier-id specified in the allocation policy
 * file else zero for default allocation
 *
 ***********************************************************/
unsigned char get_tier_gid(gid_t gidx)
{
	int i;

	if (ohsm_allocpol->cnt_groups == 0)
		return 0;

	for (i = 0; i < ohsm_allocpol->cnt_groups; i++)
		if (gidx == ohsm_allocpol->gid_tier[i].gid)
			return ohsm_allocpol->gid_tier[i].tier;
	return 0;
}

/************************************************************
 *
 * ohsm_get_tier () provides the tier id for the allocation
 * file.
 *
 * returns the tier-id specified in the allocation policy
 * file else zero for default allocation.
 * (first according to type then uid then gid)
 *
 ***********************************************************/

unsigned char ohsm_get_tier(struct dentry *dentry)
{
	char nam[256];
	unsigned char retval;

	strncpy(nam, dentry->d_name.name, dentry->d_name.len);
	retval = get_tier_name(nam, dentry->d_name.len);
	if (retval)
		return retval;

	retval = get_tier_uid(current->real_cred->uid);
	if (retval)
		return retval;

	retval = get_tier_gid(current->real_cred->gid);
	if (retval)
		return retval;
	return 0;
}
